#ifndef MW_OBJECT_H
#define MW_OBJECT_H


/*
  Meanwhile2 - Unofficial Lotus Sametime client library
  
  Copyright (c) 2007 Christopher O'Brien <siege@preoccupied.net>
  
  License: LGPL

  This library is free software; you can redistribute it and/or modify
  it under the terms of the GNU Library General Public License as
  published by the Free Software Foundation; either version 2 of the
  License, or (at your option) any later version.
                                                                                
  This library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  USA
*/


/** @file mw-object.h
    @since 2.0.0

    Base class for object types defined in the Meanwhile library. Also
    includes some utility functions for working with objects. */


#include <glib.h>
#include <glib-object.h>


G_BEGIN_DECLS


/** Base class for statefull objects. A MwObject is considered to be
    a representation of state on the server, such as a channel or a
    buddy, or the session itself. */
typedef struct mw_object {
  GObject gobject;

  gint state;
} MwObject;


/** The MwObject class */
typedef struct mw_object_class {
  GObjectClass gobjectclass;

  void (*get_state)(MwObject *, GValue *);
  void (*set_state)(MwObject *, const GValue *);
  guint signal_state_changed;
} MwObjectClass;


#define MW_TYPE_OBJECT (mw_object_get_type())


#define MW_OBJECT(obj)						\
  (G_TYPE_CHECK_INSTANCE_CAST((obj), MW_TYPE_OBJECT, MwObject))


#define MW_IS_OBJECT(obj)				\
  (G_TYPE_CHECK_INSTANCE_TYPE((obj), MW_TYPE_OBJECT))


#define MW_IS_OBJECT_CLASS(klass)			\
  (G_TYPE_CHECK_CLASS_TYPE((klass), MW_TYPE_OBJECT))


#define MW_OBJECT_GET_CLASS(obj)					\
  (G_TYPE_INSTANCE_GET_CLASS((obj), MW_TYPE_OBJECT, MwObjectClass))


GType mw_object_get_type();


void mw_object_get_state(MwObject *obj, GValue *val);


void mw_object_set_state(MwObject *obj, const GValue *val);



/** The generalized states available to a given MwObject. Each
    MwObject is considered in some way to be a representation of some
    server-side data. Objects begin inactive, or stopped. As they
    become active, they will begin to reflect events and information
    related to their server-side representation.

    Subclasses of MwObject may choose to provide their own state
    enumeration with different names in the event that doing so makes
    the states of that object more apparent. In doing so, care must be
    taken so that the correct general values are mapped to the
    specialized names. */
typedef enum mw_object_state {

  /** initial state for an object. Some activities will be
      unavailable, and the object can be considered "disconnected"
      from its server-side representation */
  MW_STATE_STOPPED = 0x0000,

  /** object is attempting to start. Some activities will still be
      unavailable, and the object is not yet connected to its
      server-side representation */
  MW_STATE_MASK_STARTING = 0x0100,

  /** object is active. All activities should be available, and the
      status should reflect its server-side representation */
  MW_STATE_MASK_STARTED = 0x0200,

  /** object is becoming inactive. Some activities will no longer be
      available, and the object is no longer connected with its
      server-side representation */
  MW_STATE_MASK_STOPPING = 0x0400,

  /** object is becoming inactive due to an error. Some activities
      will no longer be available. The object cannot be trused to
      represent its server-side representation */
  MW_STATE_MASK_ERROR = 0x8000,

} MwObjectState;


#define MW_TYPE_OBJECT_STATE (mw_object_state_get_type())


GType mw_object_state_get_type();


#define MW_OBJECT_STATE_IS(obj, statemask)		\
  (!! (mw_object_get_state((obj)) & (statemask)))


#define MW_OBJECT_STATE_IS_STOPPED(obj)			\
  (MW_OBJECT_STATE_IS((obj), MW_STATE_MASK_STOPPED))


#define MW_OBJECT_STATE_IS_STARTING(obj)		\
  (MW_OBJECT_STATE_IS((obj), MW_STATE_MASK_STARTING))


#define MW_OBJECT_STATE_IS_STARTED(obj)			\
  (MW_OBJECT_STATE_IS((obj), MW_STATE_MASK_STARTED))


#define MW_OBJECT_STATE_IS_STOPPING(obj)		\
  (MW_OBJECT_STATE_IS((obj), MW_STATE_MASK_STOPPING))


#define MW_OBJECT_STATE_IS_ERROR(obj)			\
  (MW_OBJECT_STATE_IS((obj), MW_STATE_MASK_ERROR))


/** @section GObject utility functions

    This is a collection of a few utility function to aide in debugging
    reference counting, to work with weak references, and to add
    properties to class definition.

    @{ */


#define MW_REF(obj) (mw_gobject_ref((obj)))


#define MW_UNREF(obj) (mw_gobject_unref((obj)))


/** increment the reference count for an object, and returns the
    object */
gpointer mw_gobject_ref(gpointer obj);


/** decrement the reference count for an object, and returns NULL */
gpointer mw_gobject_unref(gpointer obj);


/** return the reference count for an object */
guint mw_gobject_refcount(gpointer obj);


/** Set a weak pointer, which will be cleared when the object is
    destroyed */
void mw_gobject_set_weak_pointer(gpointer *wptr, const gpointer obj);


/** Clear a weak pointer, setting it to NULL */
void mw_gobject_clear_weak_pointer(gpointer *wptr);


/** Abbreviated way to add a gboolean property to a class */
#define mw_property_boolean(gclass,property_id,name,blurb,flags)	\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_boolean((name), NULL, (blurb), FALSE, (flags));	\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Abbreviated way to add a gint property to a class */
#define mw_property_int(gclass,property_id,name,blurb,flags)		\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_int((name), NULL, (blurb),			\
			    G_MININT, G_MAXINT, 0 ,(flags));		\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Abbreviated way to add an guint property to a class */
#define mw_property_uint(gclass,property_id,name,blurb,flags)		\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_uint((name), NULL, (blurb),			\
			     0, G_MAXUINT, 0, (flags));			\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Abbreviated way to add a char* property to a class */
#define mw_property_str(gclass,property_id,name,blurb,flags)		\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_string((name), NULL, (blurb), NULL, flags);	\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Abbreviated way to add a pointer property to a class */
#define mw_property_pointer(gclass,property_id,name,blurb,flags)	\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_pointer((name), NULL, (blurb), (flags));	\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Abbreviated way to add an object property to a class */
#define mw_property_object(gclass,property_id,name,blurb,type,flags)	\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_object((name), NULL, (blurb), (type), (flags));	\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Abbreviated way to add a boxed property to a class */
#define mw_property_boxed(gclass,property_id,name,blurb,type,flags)	\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_boxed((name), NULL, (blurb), (type), (flags));	\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Abbreviated way to add an enum property to a class */
#define mw_property_enum(gclass,property_id,name,blurb,type,flags)	\
  {									\
    GParamSpec *_gps;							\
    _gps = g_param_spec_enum((name), NULL, (blurb), (type), 0 (flags));	\
    g_object_class_install_property((gclass), (property_id), _gps);	\
  }


/** Similar to g_return_if_fail, requires that an object be in a
    certain state before continuing, and will return from the function
    with an optional value if it is not. */
#define mw_object_require_state(mwobj, state_req, retval...)		\
  {									\
    gpointer _mw_obj = (mwobj);						\
    gint _mw_obj_state = 0;						\
    gobject_get(G_OBJECT(_mw_obj), "state", &obj_state, NULL);		\
    if(obj_state != (state_req)) {					\
      g_warning("object " #mwobj " state is not " #state_req);		\
      return (retval);							\
    }									\
  }


/** @} */


G_END_DECLS


#endif /* MW_OBJECT_H */
